#include "dmxinterface.h"
#include "qlibrary.h"
#include "qmessagebox.h"


#define DMX_MAX_LEVEL               255

/** convert a value from percent (double) to dmx level (int) */
#define PERCENT_TO_LEVEL(pc)     ((int)((pc*DMX_MAX_LEVEL)/100.))

/** change application channel (0 based) to DMX standard (1 based) */
#define LOGIC_TO_PHYSIC(channel)    (channel + 1)

/** @file dmxinterface.cpp This module is an interface to the DMX dll: K8062D.dll.
  * The check on device presence is here.
  */

/* dll redefinition */
typedef void (DLL_IMPORT *K8062_StartDevice_Callback)(void);
typedef void (DLL_IMPORT *K8062_StopDevice_Callback)(void);
typedef void (DLL_IMPORT *K8062_SetData_Callback)(long , long);
typedef void (DLL_IMPORT *K8062_SetChannelCount_Callback)(long);

K8062_StartDevice_Callback K8062_StartDevice;
K8062_StopDevice_Callback K8062_StopDevice;
K8062_SetData_Callback K8062_SetData;
K8062_SetChannelCount_Callback K8062_SetChannelCount;

QLibrary    K8062lib("K8062D.dll");
int DmxInterface::m_numOfChannels = 0;

/** flag for dll found */
bool DmxInterface::m_dllLoaded = false;

DmxInterface::DmxInterface(QObject *parent) :
   QObject(parent)
{
   m_dllLoaded = false;
   m_numOfChannels = 0;
}

/** dmx service initialization. If any problem is found,
  * no message is shown to user.
  * @return true if dmx service was successfully stared. This does
  *         not mean that the device is connected.
  */
bool DmxInterface::startDmxService(int numberOfChannels)
{
   K8062_StartDevice = (K8062_StartDevice_Callback)K8062lib.resolve("StartDevice");
   K8062_StopDevice = (K8062_StopDevice_Callback)K8062lib.resolve("StopDevice");
   K8062_SetData = (K8062_SetData_Callback)K8062lib.resolve("SetData");
   K8062_SetChannelCount = (K8062_SetChannelCount_Callback)K8062lib.resolve("SetChannelCount");

   m_numOfChannels = numberOfChannels;
   m_dllLoaded = false;

   if (K8062_StartDevice)
   {
      try
      {
         K8062_StartDevice();
         K8062_SetChannelCount(m_numOfChannels);
         m_dllLoaded = true;
      }
      catch(...){
         QMessageBox::information(NULL,tr("dmx error"),
                                  tr("dmx: start process not flawless"));
      }
   }

   return m_dllLoaded;
}

/** stop dmx service, (even if not started) */
void DmxInterface::stopDmxService()
{
   if (K8062_StopDevice)
   {
      K8062_StopDevice();
   }

   m_dllLoaded = false;
}

/** @par value: dmx level in percent. If out of 0%..100%, value is clipped
  * @par channel: range 0 to DMX_NUMBER_OF_CHANNELS. If wrong, nothing is done
  */
void DmxInterface::setValueForChannel(double value, int channel)
{
   if (m_dllLoaded)
   {
      if (channel <= m_numOfChannels)
      {
         int dmxLevel = PERCENT_TO_LEVEL(constrainedValue(value));
         K8062_SetData( LOGIC_TO_PHYSIC(channel), dmxLevel);
      }
   }
}

void DmxInterface::setValueForAllChannels(QList<double> values)
{
   for (int i=0; i < values.length(); i++)
   {
      setValueForChannel( values.at(i), i);
   }
}

void DmxInterface::turnAllChannelsOff()
{
   for (int i=0; i < m_numOfChannels; i++)
   {
      setValueForChannel( 0, i);
   }
}

/** @return value if in range 0% .. 100%,
  * otherwise return the value clipped
  */
double inline DmxInterface::constrainedValue(double value)
{
   double constrained  = value;

   if (constrained < 0.0)
   {
      constrained = 0.0;
   }
   else if (constrained > 100.0)
   {
      constrained = 100.0;
   }

   return constrained;
}

/** utility for @ref dmxInterface::setValueList.
  * no check on parameters.
  */
void DmxInterface::applyValues(const double *valueList, int channels)
{
   for (int i = 0; i < channels; i++)
   {
      int dmxLevel = PERCENT_TO_LEVEL(constrainedValue(valueList[i]));
      K8062_SetData( i, dmxLevel);
   }
}

